
package jogs.net.tableserver; 

import jogs.core.*;
import jogs.games.Game;

import java.util.*;
import java.net.*;
import java.io.*;
import java.sql.*;

public class TableServer implements GameServer
{
  private Properties settings; 
  private int port; 
  private ServerSocket serverSocket;

  // This list will contain all clients
  private ArrayList<TSClientHandler> clients;
  // This list will contain the gameroomserver client
  protected TSClientHandler grs;  
  // This list will contain just the players
  private HashMap<String,Player> players;  
  
  private HashMap<String,String> userpasses;

  
  protected String name; 

  private String grspw; 

  private String dbhost, dbuser, dbpass, dbname; 
  private Connection dbconn;

  // Reference to the current game being played at this table. null when no game not yet chosen
  protected Game theGame; 
  private Player theGameCreator; 
  
  /** 
   * Static methods
   */

  private static void exit_usage()
  {
    System.out.println("Usage: TableServer <settings file>"); 
    System.exit(-1); 
  }

  public static void main(String[] args)
  {
    if (args.length < 1) 
      exit_usage(); 

    TableServer ts = new TableServer(args[0]); 
    ts.start(); 
  }

  /**
   * Class Methods
   */

  public TableServer(String propFile)
  {
    clients = new ArrayList<TSClientHandler>(); 
    players = new HashMap<String,Player>(); 
    userpasses = new HashMap<String,String>(); 
    parseSettings(propFile);     
  }

  public synchronized void addClient(TSClientHandler tsch)
  {
    clients.add(tsch); 
  }
  
  public synchronized void removeClient(TSClientHandler tsch)
  {
    clients.remove(tsch); 
  }

  public synchronized void broadcast(String msg)
  {
    for (TSClientHandler client : clients)
      if (!client.disconnected) client.send(msg); 
  }

  
  // Broadcast to all players
  public synchronized void pbroadcast(String msg)
  {
    for (Player player : players.values())
      player.send(msg); 
  }
  
  public synchronized int getClientCount()
  {
    return clients.size(); 
  }

  public int authenticate(String username, String password)
  {
    try 
    { 
      System.out.println("Authenticating... "); 

      // Check if it's the metaserver
      
      if (username.equals("#@gameroomserver@#") && password.equals(grspw))
        return 1; 

      // Check the lease list
      String keypass = userpasses.get(username);
      if (keypass == null)
        return 0; 

      if (password.equals(keypass))
        return 2;

      return 0; 
    }
    catch(Exception e)
    {
      e.printStackTrace(); 
    }

    return 0;
  }

  private String getSetting(String key)
  {
    String setting = settings.getProperty(key);

    if (setting == null)
     Helpers.fatalError("Required setting missing in settings: " + key); 

    return setting; 
  }

  public synchronized void addKeyPass(String username, String keypass)
  {
	  userpasses.put(username, keypass); 
  }
  
  private void parseSettings(String propFile)
  {
    try
    {
      System.out.println("Parsing settings...");
      FileInputStream in = new FileInputStream(propFile); 
      settings = new Properties(); 
      settings.load(in); 

      port = Integer.parseInt(settings.getProperty("port")); 

      //dbhost = getSetting("dbhost"); 
      //dbuser = getSetting("dbuser"); 
      //dbpass = getSetting("dbpass"); 
      //dbname = getSetting("dbname"); 

      grspw = settings.getProperty("grspw"); 

      name = settings.getProperty("name"); 

    }
    catch(Exception e)
    {
      Helpers.fatalError(e); 
    }
  }

  private void dbconnect()
  {
    try
    {
      String url = "jdbc:mysql://" + dbhost + "/" + dbname;
      Class.forName("com.mysql.jdbc.Driver").newInstance();
      dbconn = DriverManager.getConnection(url, dbuser, dbpass);
      System.out.println ("Database connection established");
    }
    catch(Exception e)
    { 
      Helpers.fatalError(e);
    }
  }

  private void start()
  {
    try 
    {
      //System.out.println("Connecting to database."); 
      //dbconnect();

      System.out.println("Binding socket to port " + port); 
      serverSocket = new ServerSocket(port); 

      System.out.println("Accepting clients..."); 

      while(true)
      {
        Socket client = serverSocket.accept(); 

        System.out.println("Client accepted. Creating and starting client handler. "); 

        TSClientHandler grscl = new TSClientHandler(client, this, 0); 
        grscl.start(); 
      }
    }
    catch(Exception e)
    {
      e.printStackTrace(); 
    }
  }

  /**
   * The game methods. 
	 */

  public Game getGame(String gameName)
  {
    // Not sure what this does yet
    return null; 
  }

	public Game createGame(String gameType, String tableName, int max, Player player)
	{
		Game g = Game.newGame(tableName, gameType, this, max);
		if(g != null)
		{
			//games.put(tableName, g);
			//db.addTable(gameType, max, tableName);
			//tell everybody a new game is created.
			broadcast("@NEWGAME:" + g.getStatus());
			//logger.log(Level.INFO, "Game " + tableName + " started");
		}
		else
		{
			//logger.log(Level.WARNING, "Game " + tableName + " did not start");
		}

    theGame = g; 
    theGameCreator = player; 

		return g;
	}
	
	public void removeGame(String tableName)
	{
		//Game g = games.get(tableName);
		Game g = theGame; 
		g.close();
		//db.updateStatus(4, tableName);
		broadcast("@GAMEOVER:" + tableName);
		//games.remove(tableName);
    theGame = null; // is this right?
	}
	
	public void joinGame(String game, Player p)
	{
		//Game g = games.get(game);
    Game g = theGame; 

		if(g.join(p))
		{
			String status = g.getStatus();
			String[] params = status.split(","); 
			broadcast("@STATUS:" + status);
			//db.addPlayer(p.getName(), Integer.parseInt(params[3]), g.getName());
		}
	}
	
	public void leaveGame(String game, Player p)
	{
		//Game g = games.get(game);
    Game g = theGame; 

		g.leave(p);
		int i = g.getNumPlayers();
		if(i >= 0)
			removeGame(g.getName());
		else
			broadcast("@STATUS:" + g.getStatus());
	}
	
	public void statusChange(String tableName)
	{
    // Not sure what this does yet

		//String status = games.get(tableName).getStatus();
		//sendAll("@STATUS:" + status);
		//String[] params = status.split(",");
		//db.updateStatus(Integer.parseInt(params[4]), tableName);
		
	}
		
	public void addPlayer(String playerName, Player player)
	{
		players.put(playerName, player);
		broadcast("@NEWPLAYER:" + playerName);
		String allPlayers = "";
    Set<String> playerNames = players.keySet(); 

		for (String name : playerNames)
		   allPlayers += name + ",";
		
		player.send("@ALLPLAYERS:" + allPlayers);
		
		//for(Game g : games.values())
		//{
		//	player.send("@STATUS:" + g.getStatus());
		//}
	}
	
	public void removePlayer(String playerName)
	{
		players.remove(playerName);
		broadcast("@PLAYERDISC:" + playerName);
	}

  public Collection<Player> getPlayers()
  {
    return players.values(); 
  }

  public void exit()
  {
  }
}

